﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using GraphViewer.Entity;
using System.IO;
using System.Xml.Linq;
using GraphViewer.Data;

namespace GraphViewer.View
{
    public partial class FormComparer : Form
    {
        private string _winMergeFile = Path.Combine(Environment.CurrentDirectory, @"WinMerge\WinMergeU.exe");
        private string _localFileName;
        private string _ctmFileName;
        private List<string> _localGroups;
        private List<string> _ctmGroups;
        private List<ProcessEntity> _localProcesses;
        private List<ProcessEntity> _controlMProcesses;
        private bool _isValid = true;

        public FormComparer(List<ProcessEntity> localProcesses, List<ProcessEntity> controlMProcesses, string ctmFileName)
        {
            InitializeComponent();

            _ctmFileName = ctmFileName;
            _localProcesses = localProcesses;
            _controlMProcesses = controlMProcesses;
            _localGroups = _localProcesses.Select(p => p.GroupId).Distinct().ToList();
            _ctmGroups = _controlMProcesses.Select(p => p.GroupId).Distinct().ToList();

            PopulateGroupCounters();
            PopulateProcessCounters();
            PopulateNodeGroups();
            PopulateNodeOnlyProcesses();
            PopulateNodePredecessorsProcesses();
            PopulateNodeSuccessorsProcesses();

            lblIgual.Visible = _isValid;
            lblDiferente.Visible = !_isValid;
        }

        private void PopulateGroupCounters()
        {
            lblQtdeGruposRisco.Text = _localGroups.Count.ToString();
            lblQtdeGruposCtm.Text = _ctmGroups.Count.ToString();

            if (_isValid) _isValid = _localGroups.Count == _ctmGroups.Count;

            lblQtdeGruposRisco.ForeColor = !_isValid ? Color.Red : Color.Green;
            lblQtdeGruposCtm.ForeColor = !_isValid ? Color.Red : Color.Green;
        }

        private void PopulateProcessCounters()
        {
            lblQtdeProcessosRisco.Text = _localProcesses.Count(p => !p.IsExternalJob).ToString();
            lblQtdeProcessosCtm.Text = _controlMProcesses.Count.ToString();

            if (_isValid) _isValid = _localProcesses.Count(p => !p.IsExternalJob) == _controlMProcesses.Count;

            lblQtdeProcessosRisco.ForeColor = !_isValid ? Color.Red : Color.Green;
            lblQtdeProcessosCtm.ForeColor = !_isValid ? Color.Red : Color.Green;
        }

        private void PopulateNodeGroups()
        {
            var onlyLocalGroups = LeftJoin(_localGroups, _ctmGroups);
            var onlyCtmGroups = LeftJoin(_ctmGroups, _localGroups);

            foreach (var g in onlyLocalGroups)
            {
                if (!string.IsNullOrWhiteSpace(g))
                {
                    trvRisco.Nodes[0].Nodes.Add(g);
                }
            }

            foreach (var g in onlyCtmGroups)
            {
                if (!string.IsNullOrWhiteSpace(g))
                {
                    trvCtm.Nodes[0].Nodes.Add(g);
                }
            }

            trvRisco.Nodes[0].Expand();
            trvCtm.Nodes[0].Expand();

            if (_isValid) _isValid = trvRisco.Nodes[0].Nodes.Count == 0 && trvCtm.Nodes[0].Nodes.Count == 0;
        }

        private void PopulateNodeOnlyProcesses()
        {
            var onlyLocalProcesses = from p in _localProcesses
                                     join d in _controlMProcesses
                                             on ReplaceJobName(p.ProcessId) equals ReplaceJobName(d.ProcessId) into pd
                                     from d1 in pd.DefaultIfEmpty()
                                     where d1 == null && !p.IsExternalJob
                                     select p;

            var onlyCtmProcesses = from p in _controlMProcesses
                                   join d in _localProcesses
                                           on ReplaceJobName(p.ProcessId) equals ReplaceJobName(d.ProcessId) into pd
                                   from d1 in pd.DefaultIfEmpty()
                                   where d1 == null && !p.IsExternalJob
                                   select p;

            foreach (var p in onlyLocalProcesses)
            {
                trvRisco.Nodes[1].Nodes.Add(p.ProcessId);
            }

            foreach (var p in onlyCtmProcesses)
            {
                trvCtm.Nodes[1].Nodes.Add(p.ProcessId);
            }

            trvRisco.Nodes[1].Expand();
            trvCtm.Nodes[1].Expand();

            if (_isValid) _isValid = trvRisco.Nodes[1].Nodes.Count == 0 && trvCtm.Nodes[1].Nodes.Count == 0;
        }

        private void PopulateNodePredecessorsProcesses()
        {
            var processes = from p in _localProcesses
                            join d in _controlMProcesses
                                    on ReplaceJobName(p.ProcessId) equals ReplaceJobName(d.ProcessId) into pd
                            from d1 in pd.DefaultIfEmpty()
                            where d1 != null && p.Predecessors.Count != d1.Predecessors.Count
                            select new { LocalProcess = p, CtmProcess = d1 };

            foreach (var p in processes)
            {
                var onlyLocal = LeftJoin(p.LocalProcess.Predecessors, p.CtmProcess.Predecessors);
                var onlyCtm = LeftJoin(p.CtmProcess.Predecessors, p.LocalProcess.Predecessors);
                AddNode(p.LocalProcess.ToString(), onlyLocal, onlyCtm, trvRisco.Nodes[2], trvCtm.Nodes[2]);
            }

            trvRisco.Nodes[2].Expand();
            trvCtm.Nodes[2].Expand();

            if (_isValid) _isValid = trvRisco.Nodes[2].Nodes.Count == 0 && trvCtm.Nodes[2].Nodes.Count == 0;
        }

        private void PopulateNodeSuccessorsProcesses()
        {
            var processes = from p in _localProcesses
                            join d in _controlMProcesses
                                    on ReplaceJobName(p.ProcessId) equals ReplaceJobName(d.ProcessId) into pd
                            from d1 in pd.DefaultIfEmpty()
                            where d1 != null && p.Successors.Count != d1.Successors.Count
                            select new { LocalProcess = p, CtmProcess = d1 };

            foreach (var p in processes)
            {
                var onlyLocal = LeftJoin(p.LocalProcess.Successors, p.CtmProcess.Successors);
                var onlyCtm = LeftJoin(p.CtmProcess.Successors, p.LocalProcess.Successors);
                AddNode(p.LocalProcess.ToString(), onlyLocal, onlyCtm, trvRisco.Nodes[3], trvCtm.Nodes[3]);
            }

            trvRisco.Nodes[3].Expand();
            trvCtm.Nodes[3].Expand();

            if (_isValid) _isValid = trvRisco.Nodes[3].Nodes.Count == 0 && trvCtm.Nodes[3].Nodes.Count == 0;
        }

        private void AddNode(string process, IEnumerable<string> localSuccessors, IEnumerable<string> ctmSuccessors, TreeNode locaParentNode, TreeNode ctmParentNode)
        {
            TreeNode localNode = new TreeNode(process);

            foreach (var item in localSuccessors.OrderBy(o => o))
            {
                localNode.Nodes.Add(item);
            }

            locaParentNode.Nodes.Add(localNode);

            TreeNode ctmNode = new TreeNode(process);

            foreach (var item in ctmSuccessors.OrderBy(o => o))
            {
                ctmNode.Nodes.Add(item);
            }

            ctmParentNode.Nodes.Add(ctmNode);
        }

        private IEnumerable<string> LeftJoin(IEnumerable<string> a, IEnumerable<string> b)
        {
            return from s in a
                   join d in b
                           on ReplaceJobName(s) equals ReplaceJobName(d) into pd
                   from d1 in pd.DefaultIfEmpty()
                   where d1 == null
                   select s;
        }

        private string ReplaceJobName(string jobName)
        {
            string name = jobName;

            if (jobName.EndsWith("DA"))
            {
                name = jobName.Replace("DA", "DP");
            }

            if (jobName.EndsWith("DH"))
            {
                name = jobName.Replace("DH", "DP");
            }

            return name;
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnWinMerge_Click(object sender, EventArgs e)
        {
            if (File.Exists(_winMergeFile))
            {
                _localFileName = GetLocalFileName();

                if (!string.IsNullOrEmpty(_localFileName))
                {
                    string localFolder = Extract(_localFileName);
                    string ctmFolder = Extract(_ctmFileName);

                    OpenWinMerge(ControlMLoader.GetFileNameSorted(_localFileName), ControlMLoader.GetFileNameSorted(_ctmFileName));
                    OpenWinMerge(localFolder, ctmFolder);
                }
            }
            else
            {
                MessageBox.Show(string.Format("Não foi possível localizar o WinMerge: {0}", _winMergeFile));
            }
        }

        private string Extract(string fileName)
        {
            XDocument document = XDocument.Load(fileName);
            List<XElement> elements = new List<XElement>();

            var nodes = document.Descendants().Where(n => n.Name.LocalName == "JOB").OrderBy(e => e.Attribute("JOBNAME").Value);
            string folder = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Path.GetFileNameWithoutExtension(fileName));

            if (Directory.Exists(folder))
                Directory.Delete(folder, true);

            Directory.CreateDirectory(folder);

            foreach (var jobNode in nodes)
            {
                XElement element = ControlMLoader.EqualizeElement(jobNode);
                elements.Add(element);

                string f = Path.Combine(folder, jobNode.Attribute("JOBNAME").Value.ToString() + ".xml");

                if (File.Exists(f))
                    File.Delete(f);

                File.AppendAllText(f, element.ToString());
            }

            ControlMLoader.Save(document, elements, fileName);

            return folder;
        }

        private string GetLocalFileName()
        {
            string fileName = string.Empty;

            OpenFileDialog dialog = new OpenFileDialog();

            dialog.Filter = "XML Files (*.xml)|*.xml";
            dialog.Title = "Selecione o arquivo a ser comparado.";
            dialog.RestoreDirectory = true;

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                fileName = dialog.FileName;
            }

            return fileName;
        }

        private void OpenWinMerge(string localFileName, string ctmFileName)
        {
            System.Diagnostics.Process p = new System.Diagnostics.Process();
            p.StartInfo.FileName = _winMergeFile;
            p.StartInfo.Arguments = localFileName + " " + ctmFileName;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.Start();
        }
    }
}
